﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ITAP.ShprossesBuilder.Modelling.Components;
using ITAP.ShprossesBuilder.Modelling.Components.Actions;
using ITAP.ShprossesBuilder.Modelling;
using ITAP.ShprossesBuilder.Calculations;
using ITAP.ShprossesBuilder.Properties;
using ITAP.ShprossesBuilder.Editor;
using ITAP.Core.Modelling;
using ITAP.Core.Calculations;

namespace ITAP.ShprossesBuilder.Operations
{
    public class OperationCreateShprossDiamondByShprossConnector : IOperation
    {
        #region IOperation Members

        public string Name
        {
            get { return "Создать ромб по нижнему коннектору"; }
        }

        public OperationGroup OperationGroup
        {
            get { return OperationGroup.ShprossConnectorOperation; }
        }

        public System.Drawing.Image Thumbnail
        {
            get { return Resources.AlignShprossConnectorsByDiamondDown.ToBitmap(); }
        }

        public void Execute(ModelControl modelControl)
        {
            DiamondAlignmentForm diamondForm = new DiamondAlignmentForm(false, false);
            if (diamondForm.ShowDialog() != System.Windows.Forms.DialogResult.OK)
                return;


            DiamondMeasure measure = diamondForm.DiamondAlignmentMeasure;
            double distance = diamondForm.Distance;

            int shprossConnectorCounter = 0;
            List<IComponent> componentsToProceed = new List<IComponent>();

            // Находим первые четыре выделенных соединения, из которых будем составлять ромб.
            foreach (IComponent selectedComponent in modelControl.ModelContainer.SelectedComponentsStack)
            {
                if (shprossConnectorCounter == 4)
                    break;

                if (selectedComponent is ShprossConnector)
                {
                    componentsToProceed.Add(selectedComponent);
                    shprossConnectorCounter++;
                }
            }

            // Находим центр будущего ромба.
            double minX = double.MaxValue;
            double maxX = double.MinValue;
            double minY = double.MaxValue;
            double maxY = double.MinValue;

            ShprossConnector right = null;
            ShprossConnector up = null;
            ShprossConnector left = null;
            ShprossConnector down = null;

            foreach (IComponent shprossConnector in componentsToProceed)
            {
                if (shprossConnector.Position.X < minX)
                {
                    minX = shprossConnector.Position.X;
                    left = shprossConnector as ShprossConnector;
                }
                if (shprossConnector.Position.X > maxX)
                {
                    maxX = shprossConnector.Position.X;
                    right = shprossConnector as ShprossConnector;
                }
                if (shprossConnector.Position.Y < minY)
                {
                    minY = shprossConnector.Position.Y;
                    down = shprossConnector as ShprossConnector;
                }
                if (shprossConnector.Position.Y > maxY)
                {
                    maxY = shprossConnector.Position.Y;
                    up = shprossConnector as ShprossConnector;
                }
            }
            ModelPoint center = new ModelPoint((minX + maxX) / 2, (minY + maxY) / 2);
            // Проверяем, можно ли составить из коннекторов ромб (проверяем углы).
            double totalAngle = 0;

            foreach (IComponent shprossConnector in componentsToProceed)
                totalAngle += this.GetAngleBetweenNearestConnectors((shprossConnector as ShprossConnector), center).Value;

            // Если из коннекторов невозможно составить ромб (сумма углов не равна 2 * Math.PI), ничего не удалось.
            if (!SimpleEqualProvider.IsEqual(totalAngle, 2 * Math.PI, 0.000001))
                return;

            List<ShprossConnector> shprossConnectors = new List<ShprossConnector>();
            shprossConnectors.Add(down);
            shprossConnectors.Add(right);
            shprossConnectors.Add(up);
            shprossConnectors.Add(left);

            // Находим 
            switch (measure)
            {
                case DiamondMeasure.Width:
                    this.WidthAlign(shprossConnectors, distance, center);
                    break;
                case DiamondMeasure.Height:
                    this.HeightAlign(shprossConnectors, distance, center);
                    break;
                case DiamondMeasure.DiamondBorder:
                    this.DiamondBorderAlign(shprossConnectors, distance, center);
                    break;
            }

        }

        public bool CanUndo
        {
            get { return true; }
        }

        #endregion

        private void WidthAlign(List<ShprossConnector> shprossConnectors, double distance, ModelPoint referencePoint)
        {
            ShprossConnector mainConnector = shprossConnectors[0];
            Angle connectorAngle = this.GetAngleBetweenNearestConnectors(mainConnector, referencePoint);
            Angle rightConnectorAngle = this.GetAngleBetweenNearestConnectors(shprossConnectors[1], referencePoint);
            ModelVector vector = new ModelVector(0, 1);
            double length = (distance / 2) / Math.Cos(rightConnectorAngle.Value / 2);
            vector.Length = length;
            vector = ModelCalculations.RotateVector(vector, connectorAngle.Value / 2);
            ModelPoint leftPoint = ModelCalculations.GetOffsetPoint(mainConnector.Position, vector);
            shprossConnectors[3].MoveTo(leftPoint);
            vector = new ModelVector(0, 1);
            vector.Length = length;
            vector = ModelCalculations.RotateVector(vector, -connectorAngle.Value / 2);
            ModelPoint rightPoint = ModelCalculations.GetOffsetPoint(mainConnector.Position, vector);
            shprossConnectors[1].MoveTo(rightPoint);
            vector = new ModelVector(0, 1);
            vector.Length = 2 * length * Math.Cos(connectorAngle.Value / 2);
            ModelPoint upPoint = ModelCalculations.GetOffsetPoint(mainConnector.Position, vector);
            shprossConnectors[2].MoveTo(upPoint);
        }

        private void HeightAlign(List<ShprossConnector> shprossConnectors, double distance, ModelPoint referencePoint)
        {
            ShprossConnector mainConnector = shprossConnectors[0];
            Angle connectorAngle = this.GetAngleBetweenNearestConnectors(mainConnector, referencePoint);
            Angle rightConnectorAngle = this.GetAngleBetweenNearestConnectors(shprossConnectors[1], referencePoint);
            ModelVector vector = new ModelVector(0, 1);
            double length = (distance / 2) / Math.Cos(connectorAngle.Value / 2);
            vector.Length = length;
            vector = ModelCalculations.RotateVector(vector, connectorAngle.Value / 2);
            ModelPoint leftPoint = ModelCalculations.GetOffsetPoint(mainConnector.Position, vector);
            shprossConnectors[3].MoveTo(leftPoint);
            vector = new ModelVector(0, 1);
            vector.Length = length;
            vector = ModelCalculations.RotateVector(vector, -connectorAngle.Value / 2);
            ModelPoint rightPoint = ModelCalculations.GetOffsetPoint(mainConnector.Position, vector);
            shprossConnectors[1].MoveTo(rightPoint);
            vector = new ModelVector(0, 1);
            vector.Length = 2 * length * Math.Cos(connectorAngle.Value / 2);
            ModelPoint upPoint = ModelCalculations.GetOffsetPoint(mainConnector.Position, vector);
            shprossConnectors[2].MoveTo(upPoint);
        }

        private void DiamondBorderAlign(List<ShprossConnector> shprossConnectors, double distance, ModelPoint referencePoint)
        {
            ShprossConnector mainConnector = shprossConnectors[0];
            Angle connectorAngle = this.GetAngleBetweenNearestConnectors(mainConnector, referencePoint);
            Angle rightConnectorAngle = this.GetAngleBetweenNearestConnectors(shprossConnectors[1], referencePoint);
            ModelVector vector = new ModelVector(0, 1);
            double length = distance;
            vector.Length = length;
            vector = ModelCalculations.RotateVector(vector, connectorAngle.Value / 2);
            ModelPoint leftPoint = ModelCalculations.GetOffsetPoint(mainConnector.Position, vector);
            shprossConnectors[3].MoveTo(leftPoint);
            vector = new ModelVector(0, 1);
            vector.Length = length;
            vector = ModelCalculations.RotateVector(vector, -connectorAngle.Value / 2);
            ModelPoint rightPoint = ModelCalculations.GetOffsetPoint(mainConnector.Position, vector);
            shprossConnectors[1].MoveTo(rightPoint);
            vector = new ModelVector(0, 1);
            vector.Length = 2 * length * Math.Cos(connectorAngle.Value / 2);
            ModelPoint upPoint = ModelCalculations.GetOffsetPoint(mainConnector.Position, vector);
            shprossConnectors[2].MoveTo(upPoint);
        }

        private Angle GetAngleBetweenNearestConnectors(ShprossConnector shprossConnector, ModelPoint referencePoint)
        {
            Angle referencePointAngle =
                ModelCalculations.GetAngleBetweenVectors(shprossConnector.DirectionVector, new ModelVector(shprossConnector.Position, referencePoint), false);

            for (int i = 0; i < shprossConnector.AllowedConnectorsAngles.Count; i++)
            {
                AngleInterval interval = shprossConnector.AllowedConnectorsAngles[i];
                AngleInterval nextInterval;
                if (i == shprossConnector.AllowedConnectorsAngles.Count - 1)
                    nextInterval = shprossConnector.AllowedConnectorsAngles[0];
                else
                    nextInterval = shprossConnector.AllowedConnectorsAngles[i + 1];

                AngleInterval currentInterval = new AngleInterval(interval.MiddleAngle, nextInterval.MiddleAngle);
                if (currentInterval.IsAngleInInterval(referencePointAngle))
                    return currentInterval.Angle;
            }
            throw new Exception("Угол не был найден.");
        }

    }
}
